from PyQt4.QtGui import QComboBox, QDialog, QGridLayout, QLabel, QLineEdit, \
                        QMenu, QMessageBox, QPixmap, QPushButton, QWidget
from PyQt4.QtCore import QAbstractItemModel, QModelIndex, Qt, QVariant, SIGNAL
import os

## -- Personnel Selector ------------------------------------------------------
class PersonnelSelector(QComboBox):
    def __init__(self, dbconn=None, group=None, parent=None):
        super(PersonnelSelector, self).__init__(parent)
        
        # The database connection object required to access the required data
        self._dbconn = dbconn
        
        # Descriptors for the data currently loaded in the combo box
        self._group = group
        self._personnel_id = None
        
        self._initGui()
        self._initData()
    
    def _initGui(self):
        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        
    def _initData(self):
        self.setModel(PersonnelSelectorDataModel(self._dbconn, self._group))
        self.setCurrentIndex(-1)
        
        self.connect(self, SIGNAL("currentIndexChanged(int)"), self._updateProfileTooltip)
    
    def getPersonnelId(self):
        return self._personnel_id
    
    def setPersonnelId(self, personnel_id):
        self._personnel_id = personnel_id
    
    def _getPersonnelProfile(self):
        PersonnelViewer.getProfile(self._dbconn, self._personnel_id)
        
        self.model().emit(SIGNAL("updateDataSource()"))
        
    def _removePersonnelEntry(self):
        self._personnel_id = None
        self.setCurrentIndex(-1)
    
    def _updateProfileTooltip(self, pos_index):
        data_index = self.model().index(pos_index, 0, QModelIndex())
        tooltip = str(self.model().data(data_index, Qt.ToolTipRole).toString())
        
        self.setPersonnelId(self.model().personnel_id(pos_index))
        self.setToolTip(tooltip)
        
    def contextMenuEvent(self, evt):
        menu = QMenu()
        
        actViewProfile = menu.addAction("View profile")
        actRemoveEntry = menu.addAction("Remove current entry")
        
        if not self._personnel_id:
            actViewProfile.setEnabled(False)
            actRemoveEntry.setEnabled(False)
            
        self.connect(actViewProfile, SIGNAL("triggered()"), self._getPersonnelProfile)
        self.connect(actRemoveEntry, SIGNAL("triggered()"), self._removePersonnelEntry)
        
        menu.exec_(evt.globalPos())

class PersonnelSelectorDataModel(QAbstractItemModel):
    def __init__(self, dbconn=None, group=None, parent=None):
        super(PersonnelSelectorDataModel, self).__init__(parent)
        
        self._dbconn = dbconn
        self._group = group
        self._data_headers = ["Display Name"]
        self._data_container = self._dbconn.getPersonnelProfilesByGroup(self._group) if self._dbconn else []
        
        self.connect(self, SIGNAL("updateDataSource()"), self._updateDataSource)
        
    def nodeFromIndex(self, index):
        return index.internalPointer() \
            if index.isValid() else self._data_container
            
    def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
            return QModelIndex()
        
        if not parent.isValid():
            parent_node = self._data_container
        else:
            parent_node = self.nodeFromIndex(parent)

        child_node = parent_node[row]
        
        if child_node:
            return self.createIndex(row, column, child_node)
        else:
            return QModelIndex()
    
    def parent(self, child):
        return QModelIndex()
        
    def data(self, index, role):
        if not index.isValid():
            return QVariant()
        
        if role not in [Qt.DisplayRole, Qt.EditRole, Qt.ToolTipRole]:
            return QVariant()
        
        node = self.nodeFromIndex(index)
        
        if role in (Qt.DisplayRole, Qt.EditRole):
            if index.column() == 0:                     
                return QVariant(node["display_name"])
    
        if role == Qt.ToolTipRole:
            if index.column() == 0:
                profile_summary = """
                    <b>%s</b>
                    <hr/>
                    Org: <b>%s</b><br/>
                    Office: <b>%s</b><br/>
                    Address 1: <b>%s</b><br/>
                    Address 2: <b>%s</b><br/>
                    City: <b>%s</b><br/>
                    State: <b>%s</b><br/>
                    Zipcode: <b>%s</b><br/>
                    Phone: <b>%s</b><br/>
                    Email: <b>%s</b>
                """ % (node["display_name"], node["org"], node["office"],
                       node["address1"], node["address2"], node["city"],
                       node["state"], node["zipcode"], node["phone"], 
                       node["email"])
                
                return QVariant(profile_summary)
                                    
        return QVariant()
               
    def flags(self, index=QModelIndex()):
        if not index.isValid():
            return Qt.NoItemFlags
        
        return Qt.ItemIsEnabled|Qt.ItemIsSelectable
    
    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QVariant(self._data_headers[section])
       
        return QVariant()
   
    def rowCount(self, index):
        if index.column() > 0:
            return 0
        
        return len(self._data_container)
    
    def columnCount(self, p_index):
        return len(self._data_headers)
    
    def personnel_id(self, pos_index):
        return self._data_container[pos_index]["id"] if pos_index >= 0 else None
    
    def _updateDataSource(self):
        self._data_container = self._dbconn.getPersonnelProfilesByGroup(self._group) if self._dbconn else []
    
## -- Profile Viewer ----------------------------------------------------------
class PersonnelViewer:
    @classmethod
    def getProfile(cls, dbconn=None, personnel_id=None, parent=None):
        PersonnelViewerPanel(dbconn, personnel_id, parent)
        
class PersonnelViewerPanel(QDialog):
    def __init__(self, dbconn, personnel_id=None, parent=None):
        super(PersonnelViewerPanel, self).__init__(parent)
        
        # The database connection passed in from the FTDB application
        self._dbconn = dbconn

        # The personnel id that will be used to query the database for the desired profile
        self._personnel_id = personnel_id
                
        # Primary layout manager
        self._lm_PersonnelViewer = QGridLayout()
        
        self._lblTitle = QLabel("Title:")
        self._cboTitle = QComboBox()
        self._lblLastName = QLabel("Last Name:")
        self._txtLastName = QLineEdit()
        self._lblFirstName = QLabel("First Name:")
        self._txtFirstName = QLineEdit()
        self._lblOrganization = QLabel("Organization:")
        self._txtOrganization = QLineEdit()
        self._lblOfficeSymbol = QLabel("Office Symbol:")
        self._txtOfficeSymbol = QLineEdit()
        self._lblAddress1 = QLabel("Address 1:")
        self._txtAddress1 = QLineEdit()
        self._lblAddress2 = QLabel("Address 2:")
        self._txtAddress2 = QLineEdit()
        self._lblCity = QLabel("City:")
        self._txtCity = QLineEdit()
        self._lblState = QLabel("State:")
        self._txtState = QLineEdit()
        self._lblZipcode = QLabel("Zip:")
        self._txtZipcode = QLineEdit()
        self._lblPhone = QLabel("Phone:")
        self._txtPhone = QLineEdit()
        self._lblEmail = QLabel("Email:")
        self._txtEmail = QLineEdit()
        
        # Action Buttons
        self._pnlActionButtons = QWidget()
        self._lm_ActionButtons = QGridLayout()
        self._icoPersonnelIcon = QLabel()
        self._btnSubmit = QPushButton("Submit")
        self._btnCancel = QPushButton("Cancel")
        
        self._initGui()
        self._initData()
        
        self.show()
        self.activateWindow()
        self.raise_()
        
        if not parent:
            self.exec_()
        
    def _initGui(self):
        self._initPossibleTitles()
        self._initActionButtons()
        
        self._cboTitle.setEditable(True)
        self._cboTitle.lineEdit().setReadOnly(True)
        
        self._txtFirstName.setMaxLength(25)
        self._txtLastName.setMaxLength(50)
        self._txtAddress1.setMaxLength(100)
        self._txtAddress2.setMaxLength(100)
        self._txtCity.setMaxLength(100)
        self._txtState.setMaxLength(2)
        self._txtZipcode.setMaxLength(5)
        self._txtPhone.setMaxLength(12)
        self._txtEmail.setMaxLength(100)
        
        self._txtState.setInputMask(">AA")
        self._txtZipcode.setInputMask("99999")
        self._txtPhone.setInputMask("999-999-9999")
        
        self._lm_PersonnelViewer.addWidget(self._lblTitle, 0,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._cboTitle, 0,1)
        self._lm_PersonnelViewer.addWidget(self._lblFirstName, 1,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtFirstName, 1,1)
        self._lm_PersonnelViewer.addWidget(self._lblLastName, 2,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtLastName, 2,1)
        self._lm_PersonnelViewer.addWidget(self._lblOrganization, 3,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtOrganization, 3,1)
        self._lm_PersonnelViewer.addWidget(self._lblOfficeSymbol, 4,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtOfficeSymbol, 4,1)
        self._lm_PersonnelViewer.addWidget(self._lblAddress1, 5,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtAddress1, 5,1)
        self._lm_PersonnelViewer.addWidget(self._lblAddress2, 6,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtAddress2, 6,1)
        self._lm_PersonnelViewer.addWidget(self._lblCity, 7,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtCity, 7,1)
        self._lm_PersonnelViewer.addWidget(self._lblState, 8,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtState, 8,1)
        self._lm_PersonnelViewer.addWidget(self._lblZipcode, 9,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtZipcode, 9,1)
        self._lm_PersonnelViewer.addWidget(self._lblPhone, 10,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtPhone, 10,1)
        self._lm_PersonnelViewer.addWidget(self._lblEmail, 11,0, Qt.AlignRight)
        self._lm_PersonnelViewer.addWidget(self._txtEmail, 11,1)
        self._lm_PersonnelViewer.addWidget(self._pnlActionButtons, 13,0,1,2)
        
        self._lm_PersonnelViewer.setRowMinimumHeight(12,15)
        
        self.setLayout(self._lm_PersonnelViewer)
        self.setMinimumSize(400,385)
        self.setMaximumSize(400,385)
        self.setWindowTitle("Personnel Profile")
        self.setModal(True)
        
        self.connect(self._btnSubmit, SIGNAL("clicked()"), self._commit)
        self.connect(self._btnCancel, SIGNAL("clicked()"), self.reject)
        
    def _initActionButtons(self):
        self._icoPersonnelIcon.setPixmap(QPixmap(os.path.join(os.getenv("FTDBRESDIR"), "img/person.png")))
        
        self._lm_ActionButtons.addWidget(self._icoPersonnelIcon, 0,0, Qt.AlignLeft)
        self._lm_ActionButtons.addWidget(self._btnSubmit,0,1)
        self._lm_ActionButtons.addWidget(self._btnCancel, 0,2)
        
        self._lm_ActionButtons.setMargin(0)
        self._lm_ActionButtons.setColumnStretch(0,1)
        
        self._pnlActionButtons.setLayout(self._lm_ActionButtons)
    
    def _initPossibleTitles(self):
        self._cboTitle.clear()
                
        if os.path.exists(os.path.join(os.getenv("FTDBRESDIR"), 
                                       "dat/personnel_titles")):
            self._cboTitle.addItems(open(os.path.join(os.getenv("FTDBRESDIR"), 
                                                      "dat/personnel_titles")).read().split(":"))
        else:
            self._cboTitle.addItem("No Titles Avaialable")
        
        self._cboTitle.setCurrentIndex(-1)
    
    def _initData(self):
        if self._dbconn:
            if self._personnel_id:
                personnel_profile = self._dbconn.getPersonnelProfile(self._personnel_id)
                
                self._cboTitle.setCurrentIndex(self._cboTitle.findText(personnel_profile["title"]))
                self._txtFirstName.setText(personnel_profile["first_name"])
                self._txtLastName.setText(personnel_profile["last_name"])
                self._txtOrganization.setText(personnel_profile["org"])
                self._txtOfficeSymbol.setText(personnel_profile["office"])
                self._txtAddress1.setText(personnel_profile["address1"])
                self._txtAddress2.setText(personnel_profile["address2"])
                self._txtCity.setText(personnel_profile["city"])
                self._txtState.setText(personnel_profile["state"])
                self._txtZipcode.setText(personnel_profile["zipcode"])
                self._txtPhone.setText(personnel_profile["phone"])
                self._txtEmail.setText(personnel_profile["email"])
                
                self.setWindowTitle("Personnel Profile: %s, %s" % (personnel_profile["last_name"],
                                                                   personnel_profile["first_name"]))
            else:
                QMessageBox.critical(self, "ID Error", "ERROR: You must provide a <personnel_id> to view a profile")
                self._btnSubmit.setEnabled(False)
        else:
            QMessageBox.critical(self, "Database Connection Error", 
                                 "ERROR: You must be connected to the database to view a profile")
            self._btnSubmit.setEnabled(False)
    
    def _commit(self):
        personnel_profile = {
            "id": str(self._personnel_id),
            "title": str(self._cboTitle.currentText()).upper(),
            "first_name": str(self._txtFirstName.text()).upper(),
            "last_name": str(self._txtLastName.text()).upper(),
            "org": str(self._txtOrganization.text()).upper(),
            "office": str(self._txtOfficeSymbol.text()).upper(),
            "address1": str(self._txtAddress1.text()).upper(),
            "address2": str(self._txtAddress2.text()).upper(),
            "city": str(self._txtCity.text()).upper(),
            "state": str(self._txtState.text()).upper(),
            "zipcode": str(self._txtZipcode.text()).upper(),
            "phone": str(self._txtPhone.text()),
            "email": str(self._txtEmail.text()) 
        }
        
        self._dbconn.updatePersonnelProfile(personnel_profile)
        
        self.accept()
            
def main():
    from PyQt4.QtGui import QApplication, QHBoxLayout
    from ftdb.utils.dbutils import DatabaseConnectionGateway
    from ftdb import env
    import sys
    
    env.initFTDBEnvironment()
    
    app = QApplication(sys.argv)
    
    dbconn = DatabaseConnectionGateway.getConnection()
    panel = QWidget()
    lm = QHBoxLayout()
    personnel_selector = PersonnelSelector(dbconn, "separation engineer")
    
    lm.addWidget(personnel_selector)
    
    panel.setLayout(lm)
    panel.show()
    panel.raise_()
    
    app.exec_()

if __name__ == "__main__":
    main()